<?php
/**
 * 房间
 */
namespace app\socket;

use think\worker\Server;
use app\common\utils\RedisUtils;
use Workerman\Protocols\Websocket;
use app\service\User;
use app\common\model\UserModel;
use app\handle\Table as hTable;
use app\handle\Game as hGame;

Class TableSocket extends Socket
{
    protected $socket = 'websocket://0.0.0.0:50002';

    // 枚举方法
    public $enumClassList = [
        'doClientProxy'         => [ 'args' => 3 ],
        'getClientProxyInfo'    => [ 'args' => 1 ],
        'heartBeat'             => [ 'args' => 1 ],
        'login'                 => [ 'args' => 1 ],
        'joinGameTable'         => [ 'args' => 2 ],
        'gameBaseInfo'          => [ 'args' => 2 ],
        'gameReadyOrNot'        => [ 'args' => 2 ],
        'playerLeaveTable'      => [ 'args' => 2 ],
        'gameStart'             => [ 'args' => 2 ],
        'getGameStartBaseInfo'  => [ 'args' => 2 ],
        'autoACard'             => [ 'args' => 2 ],
        'aCard'                 => [ 'args' => 2 ],
        'disCard'               => [ 'args' => 2 ],
        'withCard'              => [ 'args' => 2 ],
        'fill'                  => [ 'args' => 3 ],
        'allin'                 => [ 'args' => 2 ],
        'settlementCurMatch'    => [ 'args' => 2 ],
        'getCurMatch'           => [ 'args' => 2 ],
        'settlementAfterExitGame'   => [ 'args' => 2 ],
        'settlementAfterStartGame'  => [ 'args' => 2 ],
        'isAutoACard'           => [ 'args' => 2 ]
    ];

    // 链接uid
    protected $connid;

    public function __construct()
    {
        parent::__construct();
        $this->worker->count = 1; // 广播进程数必须为1
        $this->worker->uidConnections = array();
    }

    // 客户端连接
    public function onConnect($conn)
    {
        $conn->websocketType = Websocket::BINARY_TYPE_ARRAYBUFFER;
        echo "客户加入牌桌uid: ".$conn->id."\n";
        $this->sendDescribe($conn);
    }

    // 接收客户端数据
    public function onMessage($conn, $data)
    {
        // 映射设置
        if (!isset($conn->uid)) {
            $conn->uid = $conn->id;
            $this->worker->uidConnections[$conn->uid] = $conn;
        }

        $conn->lastMessageTime = time();
        $this->handleMessage($conn, $data);
    }

    // 客户端关闭连接
    public function onClose($conn)
    {
        echo "客户离开牌桌id: ".$conn->id."\n";
        if (isset($conn->uid)) {
            unset($this->worker->uidConnections[$conn->uid]);
        }
    }

    // 发送信息给指定的cid
    public function sendMsgByCid($conn, $cid, $data)
    {
        if (isset($this->worker->uidConnections[$cid])) {
            $conn = $this->worker->uidConnections[$cid];
            $this->sendPush($conn, $data);
        }
    }

    // 登录游戏服务器
    public function login($args, $conn)
    {
        if (empty($args) || empty($args[0])) {
            return $this->wsFailed('游戏登录认证失败');
        }

        $uinfo = solveJwt($args[0]);
        if (empty($uinfo['id'])) {
            return $this->wsFailed('登录失败，数据解析错误');
        }

        // 获取信息
        $u_model = (new User())->u_model;
        $data = $u_model->where(['uid' => $uinfo['id']])->find();

        // 判断用户是否存在游戏服务中
        $data['isInGame'] = true;
        return $this->wsSuccess($data);
    }

    // 加入游戏牌桌 0=>token 1=>tid
    public function joinGameTable($args, $conn)
    {
        // 解析token
        $tokens = $this->tokenGetId($args[0]);
        if (empty($tokens['uid'])) {
            return $tokens;
        }
        $uid = $tokens['uid'];

        $htable = new hTable($args[1], $uid);

        // 玩家加入牌桌
        $joinResult = $htable->joinTable();
        if ($joinResult['code'] !== 200) {
            if ($joinResult['code'] === 201) {
                // 存储连接id
                $htable = new hTable($joinResult['tid'], $uid);
                $cidResult = $htable->storageConnId($conn->id);
                if (!$cidResult) {
                    $conn->close();
                    $htable->closeCidByUid();
                    return $this->wsFailed('加入牌桌错误');
                }
            }
            return $this->wsResult($joinResult);
        }

        // 存储连接id
        $cidResult = $htable->storageConnId($conn->id);
        if (!$cidResult) {
            $conn->close();
            $htable->closeCidByUid();
            return $this->wsFailed('加入牌桌错误');
        }

        // 玩家加入牌桌位置
        if (!$htable->joinTablePos()) {
            return $this->wsFailed('寻找不到位置加入牌桌失败');
        }

        // 加入牌桌成功推送信息给所有人
        $cids = $htable->getCids();
        $this->pushMsgToTablePlayers($conn, $cids, 'pushJoinTable');

        $result = $this->wsSuccess();
        $result['tid'] = $args[1];
        return $this->wsResult($result);
    }

    // 牌桌的基本信息 0=>token 1=>tid
    public function gameBaseInfo($args, $conn)
    {
        // 解析token
        $tokens = $this->tokenGetId($args[0]);
        if (empty($tokens['uid'])) {
            return $tokens;
        }
        $uid = $tokens['uid'];

        $htable = new hTable($args[1], $uid);
        $hgame  = new hGame($args[1], $uid);
        $result = $this->wsSuccess($htable->getBaseTableInfo());
        return $this->wsResult($result);
    }

    // 游戏准备或不准备 0=>uid 1=>tid
    public function gameReadyOrNot($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);
        if (!$hgame->ready()) {
            return $this->wsFailed('游戏准备失败');
        }

        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();
        $this->pushMsgToTablePlayers($conn, $cids, 'pushJoinTable');
        return $this->wsSuccess();
    }

    // 玩家离开牌桌 0=>uid 1=>tid
    public function playerLeaveTable($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $htable = new hTable($args[1], $args[0]);
        if (!$htable->leaveTable()) {
            return $this->wsFailed('游戏中无法退出');
        }

        $cids = $htable->getCids();
        $this->pushMsgToTablePlayers($conn, $cids, 'pushJoinTable');
        return $this->wsSuccess();
    }

    // 开始游戏
    public function gameStart($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);

        $result = $hgame->start();
        if ($result['code'] == 200) {
            $htable = new hTable($args[1], $args[0]);
            $cids = $htable->getCids();

            $hgame->sendHands(); // 发牌
            $hgame->buildSpeakFlow(); // 创建叫牌流程
            $hgame->buckleLowNote();  // 扣除玩家低注
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStart');
            return $this->wsSuccess();
        } else {
            return $this->wsResult($result);
        }
    }

    // 获取开始游戏的基本信息 0=>uid 1=>tid
    public function getGameStartBaseInfo($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }
        $hgame = new hGame($args[1], $args[0]);
        $result = $hgame->gameStartInfo();
        return $this->wsSuccess($result);
    }

    // 自动过牌 0=>tid 1=>uid
    public function autoACard($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);

        $result = $hgame->allinAutoACard();
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();

        if ($result['code'] == 0) {
            return $this->wsFailed($result['msg']);
        } elseif ($result['code'] == 200) {
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStart');
            return $this->wsSuccess();
        } elseif ($result['code'] == 2001) {
            // 下家已经梭哈
            $data = [
                'uid' => $result['uid']
            ];
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStartAllIn', $data);
            return $this->wsSuccess();
        } elseif ($result['code'] == 2002) {
            // 全部说话发牌
            $hgame->sendCard(); // 发牌
            $hgame->buildSpeakFlow(); // 创建叫牌流程
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStart');
            return $this->wsSuccess();
        } elseif ($result['code'] == 2003) {
            // 结算
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStartSettlement');
            return $this->wsSuccess();
        }
    }

    // 过牌 0=>uid 1=>tid
    public function aCard($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);

        $result = $hgame->aCard();
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();
        $uidArray = [
            'uid' => $args[0],
            'msg' => '过牌'
        ];

        return $this->returnResults($cids, $result, $hgame, $conn, $uidArray);
    }

    // 弃牌 0=>uid 1=>tid
    public function disCard($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);

        $result = $hgame->disCard();
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();
        $uidArray = [
            'uid' => $args[0],
            'msg' => '弃牌'
        ];

        return $this->returnResults($cids, $result, $hgame, $conn, $uidArray);
    }

    // 跟牌 0=>uid 1=>tid
    public function withCard($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);

        $result = $hgame->withCard();
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();
        $uidArray = [
            'uid' => $args[0],
            'msg' => '跟注'
        ];

        return $this->returnResults($cids, $result, $hgame, $conn, $uidArray);
    }

    // 加注 0=>uid 1=>tid 2=>倍数
    public function fill($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);

        $result = $hgame->fillingCard($args[2]);
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();
        $uidArray = [
            'uid' => $args[0],
            'msg' => '加注'
        ];

        return $this->returnResults($cids, $result, $hgame, $conn, $uidArray);
    }

    // 梭哈 0=>uid 1=>tid
    public function allin($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);

        $result = $hgame->allinCard();
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();
        $uidArray = [
            'uid' => $args[0],
            'msg' => '梭哈'
        ];

        return $this->returnResults($cids, $result, $hgame, $conn, $uidArray);
    }

    // 返回操作结果
    private function returnResults($cids, $result, $hgame, $conn, $uidArray)
    {
        if ($result['code'] == 0) {
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStart');
            return $this->wsFailed($result['msg']);
        } elseif ($result['code'] == 200) {
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStart', $uidArray);
            return $this->wsSuccess();
        } elseif ($result['code'] == 2001) {
            // 下家已经梭哈
            $data = [
                'uid' => $result['uid']
            ];
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStartAllIn', $data);
            return $this->wsSuccess(['uid' => $result['uid']]);
        } elseif ($result['code'] == 2002) {
            // 全部说话发牌
            $hgame->sendCard(); // 发牌
            $hgame->buildSpeakFlow(); // 创建叫牌流程
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStart', $uidArray);
            return $this->wsSuccess();
        } elseif ($result['code'] == 2003) {
            // 结算
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameStartSettlement');
            return $this->wsSuccess();
        }
    }

    // 结算当前牌局
    public function settlementCurMatch($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();

        $results = $hgame->settlement();
        if ($results['code'] == 200) { // 结算成功
            $this->pushMsgToTablePlayers($conn, $cids, 'pushGameSuccessSettlement');
            return $this->wsSuccess([], '结算成功');
        } else {
            return $this->wsFailed($results['msg']);
        }
    }

    // 获取当前局信息
    public function getCurMatch($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();

        return $this->wsSuccess($htable->getCurMatchResults());
    }

    // 结算之后退出游戏 0=>uid 1=>tid
    public function settlementAfterExitGame($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();

        if ($hgame->getSettlementStatus()) {
            $hgame->restart();
        }

        // 判断是否该解散房间
        if ($htable->isDestyorTable()) {
            $htanle->destroyTable();
            $this->pushMsgToTablePlayers($conn, $cids, 'pushAllLeaveRoom');
            return $this->wsSuccess();
        }

        // 退出牌桌
        if ($htable->leaveTable()) {
            return $this->wsSuccess();
        } else {
            return $this->wsFailed('退出牌桌失败');
        }
    }

    // 结算之后继续游戏 0=>uid 1=>tid
    public function settlementAfterStartGame($args, $conn)
    {
        if (empty($args[0])) {
            return $this->wsFailed('登录失败');
        }

        $hgame = new hGame($args[1], $args[0]);
        $htable = new hTable($args[1], $args[0]);
        $cids = $htable->getCids();
        $cid = $htable->getCidByUid($args[0]);

        if ($hgame->getSettlementStatus()) {
            $hgame->restart();
        }

        // 判断余额是否满足牌桌要求
        $uinfo = (new UserModel)->getBaseInfoByUid($args[0]);
        if ($uinfo['balance'] < $htable->config['min_balance']) {
            $htable->leaveTable();
            return $this->wsFailed('余额不满足牌桌要求，即将退出房间');
        }

        // 判断是否该解散房间
        if ($htable->isDestyorTable()) {
            $htable->destroyTable();
            $this->pushMsgToTablePlayers($conn, $cids, 'pushAllLeaveRoom');
            return $this->wsSuccess([], 'Leave');
        }

        // 继续游戏
        $push = [['cid' => $cid]];
        $this->pushMsgToTablePlayers($conn, $push, 'pushGameReStart');
        return $this->wsSuccess();
    }

    // 判断自动过牌
    public function isAutoACard($args, $conn)
    {
        $hgame = new hGame($args[1], $args[0]);
        $result = $hgame->isNeedAutoACard();

        if ($result) {
            return $this->wsSuccess();
        } else {
            return $this->wsFailed('无须自动过牌');
        }
    }

    // 推送消息给牌桌上的玩家
    public function pushMsgToTablePlayers($conn, $cids, $msg, $data=[])
    {
        if (empty($cids)) return ;
        foreach ($cids as $k => $v) {
            $result = $this->wsSuccess($data, $msg);
            $this->sendMsgByCid($conn, $v['cid'], $result);
        }
    }
}
